import { Injectable, Logger } from '@nestjs/common';
import { IRefreshTokenRepository, RefreshTokenData } from '../interfaces/refresh-token.interface';
import { RedisService } from '../../redis/redis.service';

@Injectable()
export class InMemoryRefreshTokenRepository implements IRefreshTokenRepository {
	private tokens = new Map<string, RefreshTokenData>();

	private getKey(userId: string, tokenId: string, strategyName: string): string {
		return `${strategyName}:${userId}:${tokenId}`;
	}

	async save(userId: string, tokenId: string, expiresAt: Date, strategyName: string): Promise<void> {
		const key = this.getKey(userId, tokenId, strategyName);
		this.tokens.set(key, {
			userId,
			tokenId,
			expiresAt,
			createdAt: new Date(),
			strategyName,
		});
	}

	async verify(userId: string, tokenId: string, strategyName: string): Promise<boolean> {
		const key = this.getKey(userId, tokenId, strategyName);
		const tokenData = this.tokens.get(key);

		if (!tokenData) {
			return false;
		}

		// 检查是否过期
		if (tokenData.expiresAt < new Date()) {
			this.tokens.delete(key);
			return false;
		}

		return true;
	}

	async revoke(userId: string, tokenId: string, strategyName: string): Promise<void> {
		const key = this.getKey(userId, tokenId, strategyName);
		this.tokens.delete(key);
	}

	async revokeAll(userId: string, strategyName: string): Promise<void> {
		const prefix = `${strategyName}:${userId}:`;
		for (const key of this.tokens.keys()) {
			if (key.startsWith(prefix)) {
				this.tokens.delete(key);
			}
		}
	}

	async cleanExpired(): Promise<void> {
		const now = new Date();
		for (const [key, tokenData] of this.tokens.entries()) {
			if (tokenData.expiresAt < now) {
				this.tokens.delete(key);
			}
		}
	}
}


@Injectable()
export class RedisRefreshTokenRepository implements IRefreshTokenRepository {
	private readonly keyPrefix = 'refresh_token';
	private readonly logger = new Logger(RedisRefreshTokenRepository.name);

	constructor(private readonly redisService: RedisService) {}

	private getKey(userId: string, tokenId: string, strategyName: string): string {
		return `${this.keyPrefix}:${strategyName}:${userId}:${tokenId}`;
	}

	private getUserPrefix(userId: string, strategyName: string): string {
		return `${this.keyPrefix}:${strategyName}:${userId}:*`;
	}

	async save(userId: string, tokenId: string, expiresAt: Date, strategyName: string): Promise<void> {
		const key = this.getKey(userId, tokenId, strategyName);
		const ttl = Math.floor((expiresAt.getTime() - Date.now()) / 1000);
		
		if (ttl <= 0) {
			this.logger.warn(`Token already expired, skipping save: ${key}`);
			return;
		}

		const tokenData: RefreshTokenData = {
			userId,
			tokenId,
			expiresAt,
			createdAt: new Date(),
			strategyName,
		};

		await this.redisService.set(key, JSON.stringify(tokenData), ttl);
	}

	async verify(userId: string, tokenId: string, strategyName: string): Promise<boolean> {
		const key = this.getKey(userId, tokenId, strategyName);
		const data = await this.redisService.get(key);
		
		if (!data) {
			return false;
		}

		try {
			const tokenData: RefreshTokenData = JSON.parse(data);
			
			// 检查是否过期（Redis TTL 应该已经处理，但双重检查）
			if (new Date(tokenData.expiresAt) < new Date()) {
				await this.redisService.del(key);
				return false;
			}

			return true;
		} catch (error) {
			this.logger.error(`Failed to parse token data for key: ${key}`, error);
			await this.redisService.del(key); // 清理损坏的数据
			return false;
		}
	}

	async revoke(userId: string, tokenId: string, strategyName: string): Promise<void> {
		const key = this.getKey(userId, tokenId, strategyName);
		await this.redisService.del(key);
	}

	async revokeAll(userId: string, strategyName: string): Promise<void> {
		const client = this.redisService.getClient();
		const pattern = this.getUserPrefix(userId, strategyName);
		
		try {
			const keys = await client.keys(pattern);
			if (keys.length > 0) {
				await client.del(...keys);
			}
		} catch (error) {
			this.logger.error(`Failed to revoke all tokens for user ${userId}`, error);
			throw error;
		}
	}

	async cleanExpired(): Promise<void> {
		// Redis 的 TTL 机制会自动清理过期的 key，无需手动清理
		// 但可以记录清理日志
		this.logger.debug('Redis TTL will automatically clean expired tokens');
	}
}
